// Copyright (C) 2012 Yury Bychenkov (http://bychenkov.com).
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//  - Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//  - Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//  - Neither the name of Yury Bychenkov nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef nunchunk_h
#define nunchunk_h

#include "common.h"
#include "mappedfile.h"
#include "concurrent.h"
#include "schema.h"
#include "logchunk.h"
#include "dictionary.h"
#include "index.h"
#include "query.h"

class NunChunk {
public:
	
	static bool build(const Schema& schema, const char* file, const LogChunk& chunk);
	
	static NunChunk* open(const char* file);

	~NunChunk();

	uint getColumnCount() const;
	const Dictionary& getDictionary(uint columnIndex) const;
	const Index& getIndex(uint columnIndex) const;
	uint getType(uint columnIndex) const;

	void close();
	bool isOpened() const;

private:
	NunChunk(MappedFile* mappedFile);

private:
	static const uint32 MAGIC = 0x8ACE;
	static const uint32 DICTIONARY_SIZE_THRESHOLD = 65536;

	#pragma pack(push, 1)
	struct ColumnEntry {
		uint32 type;

		uint64 dataOffset;
		uint32 dataSize;
	
		uint32 dataSkipTableOffset;

		uint32 dictionaryOffset;
		uint32 dictionarySize;
		uint32 dictionaryCount;

		uint32 dictionarySkipTableOffset;
		uint32 dictionarySkipStep;

		uint32 indexOffset;
		uint32 indexSize;
		uint32 indexChunkSize;
	};

	struct Header {
		uint32 magic;
		uint32 headerSize;
		uint32 columnCount;
		uint32 rowCount;
		uint32 rowSkipStep;

		ColumnEntry columns[1];
	};
	#pragma pack(pop)

	template <typename ElementTraits> static uint addColumn(Header& header, ColumnEntry& entry, uint8* data, const LogChunk& chunk, uint columnIndex);

private:
    MappedFile* mappedFile;
	const Header* header;
	const uint8* data;

	Dictionary* dictionaries;
	Index* indecies;

	static Logger logger;

};

class NunChunkProvider {
public:
	NunChunkProvider(const Schema& schema, const NunChunk& chunk);

	const Schema& getSchema() const;

	bool isIndexAvailable(uint columnIndex) const;
	uint getCardinality(uint columnIndex) const;
	template <typename ElementTraits> int find(uint columnIndex, const uint8* value) const;

	class Cursor {
	public:
		Cursor(const NunChunkProvider& p, const uint* columnMapping, uint columnCount, const uint32* rangeFilter, uint rangeFilterCount); 
		uint getValueIndex() const;
		uint32 getIndex(uint columnIndex);
		const uint8* getString(uint columnIndex);
		const uint8* getDecimal(uint columnIndex);
		const uint8* getInteger(uint columnIndex);
		bool next();

	private:
		uint valueIndex;
		uint columnCount;
		const NunChunkProvider& provider;
		Index::Iterator iterators[MAX_FILTERABLE_COLUMNS];
		const Dictionary* dictionaries[MAX_FILTERABLE_COLUMNS];
	};

private:
	const Schema& schema;
	const NunChunk& chunk;
};

#include "nunchunk.hpp"

#endif // nunchunk_h
